using System;
using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Data;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace MyControls.DataSources.CompositeHierarchicalDataSource {
    /// <summary>
    /// Composes several tabular data sources into one hierarchical datasource.
    /// </summary>
    [ParseChildren(true)]
    public class CompositeHierarchicalDataSource : HierarchicalDataSourceControl, IDataSource, IListSource {
        private DataSet _dataSet;
        private RelationCollection _relations;
        private DataSourceCollection _dataSources;

        public CompositeHierarchicalDataSource() {
        }

        /// <summary>
        /// The collection of underlying tabular data sources.
        /// </summary>
        [PersistenceMode(PersistenceMode.InnerProperty)]
        [Category("Data")]
        public DataSourceCollection DataSources {
            get {
                if (_dataSources == null) {
                    _dataSources = new DataSourceCollection(this);
                }
                return _dataSources;
            }
        }

        /// <summary>
        /// The name of the tabular view that will be the root of the hierarchical source.
        /// </summary>
        [Category("Data")]
        [DefaultValue("")]
        public string RootViewName {
            get {
                string s = (string)ViewState["RootViewName"];
                return (s == null ? String.Empty : s);
            }
            set {
                ViewState["RootViewName"] = value;
            }
        }

        /// <summary>
        /// The collection of <see cref="Relation"/>s that give the hierarchy to the collection of <see cref="DataSources"/>.
        /// </summary>
        [PersistenceMode(PersistenceMode.InnerProperty)]
        [Category("Data")]
        public RelationCollection Relations {
            get {
                if (_relations == null) {
                    _relations = new RelationCollection(this);
                }
                return _relations;
            }
        }

        /// <summary>
        /// The underlying dataset's relations
        /// </summary>
        internal DataRelationCollection AllRelations {
            get {
                return _dataSet.Relations;
            }
        }

        /// <summary>
        /// The subcontrols for a <see cref="CompositeHierarchicalDataSource"/> are the underlying <see cref="DataSources"/>.
        /// </summary>
        /// <returns>The underlying <see cref="DataSources"/>.</returns>
        protected override ControlCollection CreateControlCollection() {
            return DataSources;
        }

        /// <summary>
        /// The <see cref="System.Data.DataSet"/> that we use internally to represent the data.
        /// </summary>
        internal DataSet DataSet {
            get {
                // TODO: Lazy data-loading, to enable TreeView and other similar controls to only hit what they need when they need it.
                // TODO: allow for other types of data source.
                if (_dataSet == null) {
                    _dataSet = new DataSet();
                    foreach (IDataSource source in Controls) {
                        foreach (string viewName in source.GetViewNames()) {
                            DataSourceView view = source.GetView(viewName);
                            IEnumerable data = null;
                            SqlDataSourceView sdsv = view as SqlDataSourceView;
                            if (sdsv != null) {
                                data = sdsv.Select(DataSourceSelectArguments.Empty);
                            }
                            else {
                                ObjectDataSourceView odsv = view as ObjectDataSourceView;
                                if (odsv != null) {
                                    data = odsv.Select(DataSourceSelectArguments.Empty);
                                }
                                else {
                                    ITabularDataSourceView tdsv = view as ITabularDataSourceView;
                                    if (tdsv != null) {
                                        data = tdsv.Select(DataSourceSelectArguments.Empty);
                                    }
                                    else {
                                        throw new NotImplementedException("This control currently supports only SqlDataSource, ObjectDataSource and datasources with views that implement ITabularDataSourceView.");
                                    }
                                }
                            }
                            DataView dataView = data as DataView;
                            if (dataView != null) {
                                DataTable table = dataView.Table.Clone();
                                table.TableName = viewName;
                                table.Namespace = ((Control)source).UniqueID;
                                _dataSet.Tables.Add(table);
                                table.BeginLoadData();
                                foreach (DataRowView row in dataView) {
                                    table.ImportRow(row.Row);
                                }
                                table.EndLoadData();
                            }
                            else {
                                throw new NotImplementedException("This control currently supports only ObjectDataSource that return DataSets or DataTables");
                            }
                        }
                    }
                    foreach (Relation relation in Relations) {
                        relation.GetRelation();
                    }
                }
                return _dataSet;
            }
            set {
                _dataSet = value;
            }
        }

        /// <summary>
        /// Gets a <see cref="HierarchicalDataSourceView"/> for the provided path.
        /// </summary>
        /// <param name="viewPath">
        /// The path to the view.
        /// The path has the syntax NameSpace:TableName/Constraint,
        /// where NameSpace is the name of the underlying tabular data source,
        /// TableName is the name of the table or view, and
        /// Constraint is a string that can be understood by a dataset as the
        /// argument of a select statement.
        /// </param>
        /// <returns>A filtered view of the data for this path.</returns>
        protected override HierarchicalDataSourceView GetHierarchicalView(string viewPath) {
            if (viewPath == null || viewPath.Length == 0) {
                return new CompositeHierarchicalDataSourceView(this, RootViewName);
            }
            return new CompositeHierarchicalDataSourceView(this, viewPath);
        }

        #region State Management

        protected override void LoadViewState(object savedState) {
            Pair pairState = savedState as Pair;
            if (pairState != null) {
                base.LoadViewState(pairState.First);
                ((IStateManager)_relations).LoadViewState(pairState.Second);
            }
            else {
                base.LoadViewState(savedState);
            }
        }

        protected override object SaveViewState() {
            if (_relations != null && _relations.Count > 0) {
                return new Pair(base.SaveViewState(), ((IStateManager)_relations).SaveViewState());
            }
            return base.SaveViewState();
        }

        protected override void TrackViewState() {
            base.TrackViewState();

            if (_relations != null) {
                ((IStateManager)_relations).TrackViewState();
            }
        }
        #endregion

        #region IListSource Members
        bool IListSource.ContainsListCollection {
            get {
                if (DesignMode) {
                    return false;
                }
                return ListSourceHelper.ContainsListCollection(this);
            }
        }


        System.Collections.IList IListSource.GetList() {
            if (DesignMode) {
                return null;
            }
            return ListSourceHelper.GetList(this);
        }

        #endregion

        #region IDataSource Members
        event EventHandler IDataSource.DataSourceChanged {
            add {
                ((IHierarchicalDataSource)this).DataSourceChanged += value;
            }
            remove {
                ((IHierarchicalDataSource)this).DataSourceChanged -= value;
            }
        }


        DataSourceView IDataSource.GetView(string viewName) {
            return new TableDataSourceView(this, viewName);
        }

        System.Collections.ICollection IDataSource.GetViewNames() {
            StringCollection views = new StringCollection();
            foreach (DataTable table in this.DataSet.Tables) {
                views.Add(table.TableName);
            }
            return views;
        }

        #endregion
    }
}
